#define PrynEditorInternal
#define PrynEditorImplementation
#include <prynEditor.h>

static PrynResult ComponentChangeNotifier (PrynChange *change, void *data);

PrynEditorExport (PrynResult, prynEditorComponentFrom, (PrynEditorComponent **output, PrynEditorBoard *board, PrynComponent *component))
{
	PrynStartFunction ();
	PrynString id;
	PrynObject object;
	PrynEditorComponent *editor;
	PrynTagList *tags;

	if (output) *output = 0;
	if (board) state = board->state;
	else if (component) prynCall (prynComponentState (component, &state));
	prynAssert (board && component, NullArgument);
	
	prynCall (prynComponentTags (component, &tags));
	prynCall (prynTagFindDataByKey ((void **) &editor, tags, board));
	
	if (!editor)
	{
		PrynTag *tag;
		PrynMonitor *monitor;

		prynCall (prynAllocateClearedObject (state, &editor));
		if (!editor) return 0;
		
		editor->board = board;
		editor->component = component;
		editor->w = 100;
		prynDListAppend (board->components, editor);
		
		prynComponentToObject (component, &object);
		prynStringLiteral (&id, "PrynEditorComponent");
		prynTagCreateWithKey (&tag, &object, board, &id, editor);
		
		prynTagSetDestroyFunction (tag, prynTagFreeDataFunction ());
		prynMonitorCreate (&monitor, &object, PrynChangeTypeAll, &ComponentChangeNotifier, editor, 0);
		
		prynEditorComponentDirty (editor);
	}

	if (output) *output = editor;
	PrynEndFunction ();
}

static PrynResult ComponentChangeNotifier (PrynChange *change, void *data)
{
	PrynEditorComponent *editor = (PrynEditorComponent *) data;
	return prynEditorComponentDirty (editor);
}

PrynEditorExport (PrynResult, prynEditorComponentDirty, (PrynEditorComponent *component))
{
	int borderSize = 1;

	if (!component) return PrynResultNullArgument;
	//prynGuiControlDirty (component->board->control);
	prynGuiControlDirtyArea (component->board->control, component->x - borderSize, component->y - borderSize - 4, component->x + component->w + borderSize, component->y + (component->h ? component->h + 3 : 1000) + borderSize);
	prynEditorComponentDirtyConnections (component);
	return PrynResultSuccess;
}

static PrynResult prynFindOverlappingEditorComponent (PrynEditorComponent *first, PrynEditorComponent **pointer, const PrynRectangle *area)
{
	int left = area->left, right = area->right, top = area->top, bottom = area->bottom;
	PrynEditorComponent *component;

	if (left > right) prynSwap (int, left, right);
	if (top > bottom) prynSwap (int, top, bottom);
	
	for (component = first; component; component = component->next)
		if (component->x <= right && component->x + component->w >= left && component->y <= bottom && component->y + component->h >= top)
		{
			if (pointer) *pointer = component;
			return PrynResultSuccess;
		}
		
	if (pointer) *pointer = 0;
	return PrynResultDone;
}

PrynEditorExport (PrynResult, prynEditorBoardFirstOverlappingComponent, (PrynEditorBoard *board, PrynEditorComponent **pointer, const PrynRectangle *area))
{
	if (!board || !area)
	{
		if (pointer) *pointer = 0;
		return PrynResultNullArgument;
	}
	return prynFindOverlappingEditorComponent (board->components.first, pointer, area);
}

PrynEditorExport (PrynResult, prynEditorBoardNextOverlappingComponent, (PrynEditorComponent *reference, PrynEditorComponent **pointer, const PrynRectangle *area))
{
	if (!reference || !area)
	{
		if (pointer) *pointer = 0;
		return PrynResultNullArgument;
	}
	return prynFindOverlappingEditorComponent (reference->next, pointer, area);
}

PrynEditorExport (PrynResult, prynEditorBoardIterateOverlappingComponents, (PrynEditorBoard *board, PrynEditorComponent **component, const PrynRectangle *area))
{
	if (!component || !area) return PrynResultNullArgument;
	if (*component) return prynEditorBoardNextOverlappingComponent (*component, component, area);
	return prynEditorBoardFirstOverlappingComponent (board, component, area);
}

PrynEditorExport (PrynResult, prynEditorComponentMove, (PrynEditorComponent *component, int x, int y))
{
	if (!component) return PrynResultNullArgument;
	if (component->x == x && component->y == y) return PrynResultDone;
	prynEditorComponentDirty (component);
	component->x = x;
	component->y = y;
	prynEditorComponentDirty (component);
	prynEditorBoardDirtyConnections (component->board);
	component->board->pathDirty = true;
	prynEditorBoardUpdateConnections (component->board);
	prynEditorBoardDirtyConnections (component->board);
	return PrynResultSuccess;
}

static void dirtyPin (PrynEditorComponent *component, PrynPin *pin)
{
	PrynEditorPin *editor = 0;
	PrynEditorPathRoute *route = 0;

	prynEditorPinFrom (&editor, component, pin);
	if (editor) route = &editor->route;
	prynEditorPathRouteDirty (route, component->board->control);
}

PrynEditorExport (PrynResult, prynEditorComponentDirtyConnections, (PrynEditorComponent *component))
{
	PrynPin *pin;

	if (!component) return PrynResultNullArgument;
	for (pin = 0; !prynComponentIterateInputPins (component->component, &pin); )
	{
		PrynPin *other;
		
		prynPinConnected (pin, &other);
		
		if (other)
		{
			PrynComponent *otherComponent;
			PrynEditorComponent *editor;

			prynPinComponent (other, &otherComponent);
			prynEditorComponentFrom (&editor, component->board, otherComponent);
			dirtyPin (editor, other);
		}
	}
	
	return PrynResultSuccess;
}

PrynEditorExport (PrynResult, prynEditorComponentDirtyOutputConnections, (PrynEditorComponent *component))
{
	PrynPin *pin;

	if (!component) return PrynResultNullArgument;
	for (pin = 0; !prynComponentIterateOutputPins (component->component, &pin); )
		dirtyPin (component, pin);
	return PrynResultSuccess;
}
